using System;
using System.Drawing;
using System.Windows.Forms;
using RPGOnline.Controls.Map.Events;
using RPGOnline.Controls.Map.Layers;
using RPGOnline.Controls.Map.Windows;

namespace RPGOnline.Controls.Map
{
    public class TilesetPainter
    {
        private readonly MapControl m_mapControl;
        private TilesetPaintingForm m_tilesetPaintingForm;
        private Point m_areaPaintStart;
        private bool m_areaPainting;

        public TilesetPainter(MapControl mapControl, TilesetPaintingForm tilesetPaintingForm)
        {
            m_mapControl = mapControl;
            m_tilesetPaintingForm = tilesetPaintingForm;
            m_tilesetPaintingForm.Closing += (s, e) =>
            {
                e.Cancel = true;
                Disable();
            };
        }

    	public void Disable()
        {
            m_mapControl.OnCellMouseMove -= OnPaintMouseMove;
            m_mapControl.OnCellMouseDown -= OnPaintMouseDown;
            m_mapControl.OnCellMouseUp -= OnPaintMouseUp;

            m_mapControl.OnCellMouseMove -= OnAreaPaintMouseMove;
            m_mapControl.OnCellMouseDown -= OnAreaPaintMouseDown;
            m_mapControl.OnCellMouseUp -= OnAreaPaintMouseUp;

            m_areaPainting = false;
            m_mapControl.OverlayLayer.Clear();
        }

        public MapTileset TileSet
        {
            get { return m_tilesetPaintingForm.Tileset; }
            set { m_tilesetPaintingForm.Tileset = value; }
        }

        public void EnablePaint()
        {
            Disable();
            
            m_mapControl.OnCellMouseMove += OnPaintMouseMove;
            m_mapControl.OnCellMouseDown += OnPaintMouseDown;
            m_mapControl.OnCellMouseUp += OnPaintMouseUp;
        }

        public void OnPaintMouseDown(object sender, CellEventArgs e)
        {
            if (e.Mouse.Button != MouseButtons.Left)
                return;

            Paint(m_mapControl.Map, e.MapCell, m_mapControl.Map.CurrentLayer);
            m_mapControl.Invalidate(true);
        }

        public void OnPaintMouseMove(object sender, CellEventArgs e)
        {
            if(e.Mouse.Button == MouseButtons.None)
            {
                m_mapControl.OverlayLayer.Clear();
                Paint(m_mapControl.Map, e.MapCell, m_mapControl.OverlayLayer);
                m_mapControl.Invalidate(true);
                return;
            }
            else if (e.Mouse.Button != MouseButtons.Left)
                return;

            Paint(m_mapControl.Map, e.MapCell, m_mapControl.Map.CurrentLayer);
            m_mapControl.Invalidate(true);
        }

        public void OnPaintMouseUp(object sender, CellEventArgs e)
        {
            if (e.Mouse.Button != MouseButtons.Left)
                return;

            Paint(m_mapControl.Map, e.MapCell, m_mapControl.Map.CurrentLayer);
            m_mapControl.Invalidate(true);
        }

        public void OnAreaPaintMouseDown(object sender, CellEventArgs e)
        {
            if (e.Mouse.Button != MouseButtons.Left)
                return;

            m_areaPaintStart = e.MapCell;
            m_areaPainting = true;
        }

        public void OnAreaPaintMouseMove(object sender, CellEventArgs e)
        {
            if (!m_areaPainting)
                return;

            int left = Math.Min(m_areaPaintStart.X, e.MapCell.X);
            int right = Math.Max(m_areaPaintStart.X, e.MapCell.X);
            int top = Math.Min(m_areaPaintStart.Y, e.MapCell.Y);
            int bottom = Math.Max(m_areaPaintStart.Y, e.MapCell.Y);

            Rectangle rect = Rectangle.FromLTRB(left, top, right + 1, bottom + 1);

            if (e.Mouse.Button == MouseButtons.None || e.Mouse.Button == MouseButtons.Left)
            {
                m_mapControl.OverlayLayer.Clear();
                AreaPaint(m_mapControl.Map, rect, m_mapControl.OverlayLayer);
                m_mapControl.Invalidate(true);
            }
        }

        public void OnAreaPaintMouseUp(object sender, CellEventArgs e)
        {
            if (e.Mouse.Button != MouseButtons.Left)
                return;

            int left = Math.Min(m_areaPaintStart.X, e.MapCell.X);
            int right = Math.Max(m_areaPaintStart.X, e.MapCell.X);
            int top = Math.Min(m_areaPaintStart.Y, e.MapCell.Y);
            int bottom = Math.Max(m_areaPaintStart.Y, e.MapCell.Y);

            Rectangle rect = Rectangle.FromLTRB(left, top, right + 1, bottom + 1);

            AreaPaint(m_mapControl.Map, rect, m_mapControl.Map.CurrentLayer);
            m_areaPainting = false;
            m_mapControl.OverlayLayer.Clear();
            m_mapControl.Invalidate(true);
        }

        public void Paint(Map map, Point mapCell, ICellsLayer layer)
        {
            MapCell[,] selectedTiles = m_tilesetPaintingForm.SelectedTiles;

            int maxXCell = Math.Min(map.MapSize.Width, mapCell.X + m_tilesetPaintingForm.SelectedTilesWidth);
            int maxYCell = Math.Min(map.MapSize.Height, mapCell.Y + m_tilesetPaintingForm.SelectedTilesHeight);
            for(int x = mapCell.X; x < maxXCell; x++)
            {
                for(int y = mapCell.Y; y < maxYCell; y++)
                {
                    layer[x, y] = selectedTiles[x - mapCell.X, y - mapCell.Y];
                    //layer[x, y].Frames.Clear();
                    //layer[x, y].Frames.AddRange(selectedTiles[x - mapCell.X, y - mapCell.Y].Frames);
                }
            }
        }

        public void AreaPaint(Map map, Rectangle rectangle, ICellsLayer layer)
        {
            MapCell[,] selectedTiles = m_tilesetPaintingForm.SelectedTiles;
			if(selectedTiles == null)
				return;

            int maxXCell = Math.Min(map.MapSize.Width, rectangle.X + rectangle.Width);
            int maxYCell = Math.Min(map.MapSize.Height, rectangle.Y + rectangle.Height);

            for (int x = rectangle.X; x < maxXCell; x++)
            {
                int cellX = (x - rectangle.X)%m_tilesetPaintingForm.SelectedTilesWidth;

                for (int y = rectangle.Y; y < maxYCell; y++)
                {
                    int cellY = (y - rectangle.Y)%m_tilesetPaintingForm.SelectedTilesHeight;

                    layer[x, y].Frames.Clear();
                    layer[x, y].Frames.AddRange(selectedTiles[cellX, cellY].Frames);
                }
            }
        }

        public void EnableAreaPaint()
        {
            Disable();

            m_mapControl.OnCellMouseDown += OnAreaPaintMouseDown;
            m_mapControl.OnCellMouseMove += OnAreaPaintMouseMove;
            m_mapControl.OnCellMouseUp += OnAreaPaintMouseUp;
        }
    }
}